home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 2: CDPD 1
/
Almathera Ten on Ten - Disc 2: CDPD 1.iso
/
pd
/
201-225
/
225
/
amigatcp
/
src
/
ax25.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-13
|
13KB
|
614 lines
/* Vestigial AX.25 link layer, understands only UI frames */
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "iface.h"
#include "timer.h"
#include "arp.h"
#include "slip.h"
#include "ax25.h"
#include <ctype.h>
#ifdef TRACE
#include "trace.h"
#endif
/* AX.25 broadcast address: "QST-0" in shifted ascii */
struct ax25_addr ax25_bdcst = {
'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1,
('0'<<1) | E,
};
struct ax25_addr mycall;
int digipeat; /* Controls digipeating */
/* Send IP datagrams in AX.25 UI frames using ARP */
int
ax_send(bp,interface,gateway,precedence,delay,throughput,reliability)
struct mbuf *bp;
struct interface *interface;
int32 gateway;
char precedence;
char delay;
char throughput;
char reliability;
{
char *hw_addr,*res_arp();
hw_addr = res_arp(interface,ARP_AX25,gateway,bp);
if(hw_addr != NULLCHAR)
(*interface->output)(interface,(struct ax25_addr *)hw_addr,
interface->hwaddr,PID_IP,bp);
}
/* Send a raw packet to the KISS TNC using AX.25 link header.
* Note that the calling order here must match ec_output
* since ARP also uses it.
*/
kiss_output(interface,dest,src,pid,bp)
struct interface *interface;
struct ax25_addr *dest; /* Destination AX.25 address (7 bytes, shifted) */
struct ax25_addr *src; /* Source AX.25 address (7 bytes, shifted) */
char pid; /* Protocol ID */
struct mbuf *bp; /* Data field (follows PID) */
{
register struct mbuf *hbp;
struct mbuf *ax_encode();
struct slip *sp;
if((bp = ax_encode(dest,src,pid,bp)) == NULLBUF)
return;
#ifdef TRACE
if(trace & TRACE_AX25){
printf("%s sent:\r\n",interface->name);
if((trace & TRACE_HDR) > 1)
ax25_dump(bp);
if(trace & TRACE_DUMP)
hexdump(bp);
if(trace & TRACE_ASCII)
asciidump(bp);
fflush(stdout);
}
#endif
/* Put type field for KISS TNC on front */
if((hbp = alloc_mbuf(1)) == NULLBUF){
free_p(bp);
return;
}
*hbp->data = 0;
hbp->cnt = 1;
hbp->next = bp;
slipq(interface->dev,hbp);
}
/* Put a AX.25 header on the front of a packet */
struct mbuf *
ax_encode(dest,src,pid,bp)
struct ax25_addr *dest; /* Destination AX.25 address (7 bytes, shifted) */
struct ax25_addr *src; /* Source AX.25 address (7 bytes, shifted) */
char pid; /* Protocol ID */
struct mbuf *bp; /* Data field (follows PID) */
{
register struct ax25_addr *ap;
struct mbuf *abp;
char *cp;
int ndigi;
int16 hdr_len;
/* Determine length of dest addr */
for(ndigi = 0,ap = dest; (ap->ssid & E) == 0;
ap = (struct ax25_addr *) ((char *)ap + AXALEN))
ndigi++;
/* Compute header length:
* 2 AX.25 address fields for source and dest +
* "ndigi" AX.25 address field(s) for digipeaters +
* 2 bytes for control and PID fields
*/
hdr_len = (2 + ndigi)*AXALEN + 2;
/* Create AX.25 link level header */
if((abp = alloc_mbuf(hdr_len)) == NULLBUF){
free_p(bp);
return NULLBUF;
}
abp->cnt = hdr_len;
/* Now fill it in */
ap = (struct ax25_addr *)(abp->data);
bcopy((char *)dest,(char *)ap,AXALEN);
ap->ssid &= ~E;
ap = (struct ax25_addr *) ((char *)ap + AXALEN);
dest = (struct ax25_addr *) ((char *)dest + AXALEN);
bcopy((char *)src,(char *)ap,AXALEN);
ap->ssid &= ~E;
cp = (char *)ap; /* get pointer to last address (source) */
ap = (struct ax25_addr *) ((char *)ap + AXALEN);
while(ndigi-- != 0) {
bcopy((char *)dest,(char *)ap,AXALEN);
dest = (struct ax25_addr *) ((char *)dest + AXALEN);
cp = (char *)ap;
ap = (struct ax25_addr *) ((char *)ap + AXALEN);
}
((struct ax25_addr *)cp)->ssid |= E; /* Mark end of address field */
cp = (char *)ap; /* Point to first byte past address field */
*cp++ = UI;
*cp++ = pid;
abp->next = bp; /* Link in data field */
return abp;
}
/* Process incoming KISS TNC frame */
kiss_recv(interface,bp)
struct interface *interface;
struct mbuf *bp;
{
pullup(&bp,NULLCHAR,1); /* remove KISS TNC type field */
if(bp != NULLBUF)
ax_recv(interface,bp);
}
/* Process incoming AX.25 packets.
* After optional tracing, the address field is examined. If it is
* directed to us as a digipeater, repeat it. If it is addressed to
* us or to QST-0, kick it upstairs depending on the protocol ID.
*/
int
ax_recv(interface,bp)
struct interface *interface;
struct mbuf *bp;
{
void arp_input(),ip_route();
struct ax25_addr *ap,*ap1;
char pid,multicast,ours,*control,*cbyte();
int addrsize;
struct mbuf *hbp;
#ifdef TRACE
if(trace & TRACE_AX25){
printf("%s recv:\r\n",interface->name);
if((trace & TRACE_HDR) > 1)
ax25_dump(bp);
if(trace & TRACE_DUMP)
hexdump(bp);
if(trace & TRACE_ASCII)
asciidump(bp);
fflush(stdout);
}
#endif
control = cbyte(bp); /* control -> control byte */
ap = (struct ax25_addr *)bp->data; /* -> address field */
addrsize = control - (char *)ap; /* # bytes in address field */
/* Check for either a missing control byte or a residual length
* address field
*/
if(control == NULL || addrsize % AXALEN != 0){
free_p(bp);
return;
}
addrsize /= AXALEN; /* # addresses in address field */
/* Check for invalid address field (too short or odd length) */
if(addrsize < 2) {
free_p(bp);
return;
}
/* Rescan, looking for our call in the repeater fields, if any.
* Repeat appropriate packets.
*/
/* for(ap1 = &ap[2]; ap1 < &ap[addrsize]; ap1++){ */
for(ap1 = (struct ax25_addr *) ((char *)ap + 2*AXALEN);
ap1 < (struct ax25_addr *) ((char *)ap + addrsize*AXALEN);
ap1 = (struct ax25_addr *) ((char *)ap1 + AXALEN)) {
if((ap1->ssid & REPEATED) == 0){
/* Check if packet is directed to us as a digipeater */
if(digipeat && addreq(ap1,&mycall)){
/* Yes, kick it back out */
ap1->ssid |= REPEATED;
/* Put type field for KISS TNC on front */
if((hbp = alloc_mbuf(1)) == NULLBUF){
free_p(bp);
return;
}
*hbp->data = 0;
hbp->cnt = 1;
hbp->next = bp;
slipq(interface->dev,hbp);
} else {
/* Addressed to some other digipeater */
free_p(bp);
}
return;
}
}
/* Packet has passed all repeaters, now look at destination */
ours = 0;
if(addreq(&ap[0],&ax25_bdcst)){
multicast = 1; /* Broadcast packet */
} else if(addreq(&ap[0],&mycall)){
multicast = 0; /* Packet directed at us */
ours = 1;
#if 0
/* we really do want to see all of the packets, later on */
} else {
/* Not for us */
free_p(bp);
return;
#endif
}
/* Now remove the header and the control field. Note: This will
* have to be changed if the connected mode of AX.25 (ugh!) is ever
* implemented
*/
pullup(&bp,NULLCHAR,1 + addrsize * AXALEN);
/* Examine the protocol ID field and switch to the right protocol */
if(pullup(&bp,&pid,1) != 1)
return; /* No PID, probably not an I-frame */
switch(pid & 0xff){
case PID_ARP:
arp_input(interface,bp);
break;
case PID_IP:
if (!(ours || multicast)) {
free_p(bp);
break;
}
ip_route(bp,multicast);
break;
#ifdef NETROM
case PID_NETROM:
netrom_input(interface, bp);
break;
#endif
default:
free_p(bp);
break;
}
}
/* Display or change our AX.25 address */
domycall(argc,argv)
int argc;
char *argv[];
{
char buf[15];
if(argc < 2){
pax25(buf,&mycall);
printf("%s\r\n",buf);
return 0;
}
if(setcall(&mycall,argv[1]) == -1)
return -1;
mycall.ssid |= E;
}
/*
* setcall - convert callsign plus substation ID of the form
* "KA9Q-0" to AX.25 (shifted) address format
* Address extension bit is left clear
* Return -1 on error, 0 if OK
*/
int
setcall(out,call)
struct ax25_addr *out;
char *call;
{
int csize;
unsigned ssid;
register int i;
register char *cp,*dp;
char c,*index();
if(out == (struct ax25_addr *)NULL || call == NULLCHAR || *call == '\0'){
return -1;
}
/* Find dash, if any, separating callsign from ssid
* Then compute length of callsign field and make sure
* it isn't excessive
*/
dp = index(call,'-');
if(dp == NULLCHAR)
csize = strlen(call);
else
csize = dp - call;
if(csize > 6)
return -1;
/* Now find and convert ssid, if any */
if(dp != NULLCHAR){
dp++; /* skip dash */
ssid = atoi(dp);
if(ssid > 15)
return -1;
} else
ssid = 0;
/* Copy upper-case callsign, left shifted one bit */
cp = out->call;
for(i=0;i<csize;i++){
c = *call++;
if(islower(c))
c = toupper(c);
*cp++ = c << 1;
}
/* Pad with shifted spaces if necessary */
for(;i<6;i++)
*cp++ = ' ' << 1;
/* Insert substation ID field and set reserved bits */
out->ssid = 0x60 | (ssid << 1);
return 0;
}
static
addreq(a,b)
register struct ax25_addr *a,*b;
{
if(bcmp(a->call,b->call,ALEN) != 0)
return 0;
if((a->ssid & SSID) != (b->ssid & SSID))
return 0;
return 1;
}
/* Convert encoded AX.25 address to printable string */
pax25(e,addr)
char *e;
struct ax25_addr *addr;
{
register int i;
char c,*cp;
cp = addr->call;
for(i=6;i != 0;i--){
c = (*cp++ >> 1) & 0x7f;
if(c == ' ')
break;
*e++ = c;
}
sprintf(e,"-%d",(addr->ssid >> 1) & 0xf); /* ssid */
}
/* Print a string of AX.25 addresses in the form
* "KA9Q-0 [via N4HY-0,N2DSY-2]"
*/
psax25(e,addr)
register char *e;
register struct ax25_addr *addr;
{
int i;
for(i=0;;i++){
pax25(e,addr);
if(addr->ssid & E)
break;
if(i == 0)
strcat(e," via ");
else
strcat(e,",");
e += strlen(e);
/* addr++; */
addr = (struct ax25_addr *) ((char *)addr + AXALEN);
}
}
/* Return a pointer to the control byte in the given frame */
char *
cbyte(fp)
register struct mbuf *fp;
{
register char *cp;
register unsigned cnt;
if(fp == NULLBUF || fp->data == NULLCHAR)
return NULLCHAR;
cnt = fp->cnt;
cp = fp->data;
while(cnt != 0 && (*cp & E) == 0){
cnt--;
cp++;
}
/* If the address field never ended, cnt = 0; if it ended
* on the last byte of a frame, cnt = 1. In either case,
* there is no control field
*/
if(cnt <= 1)
return NULLCHAR;
else
return cp + 1;
}
dokiss(argc,argv)
int argc;
char *argv[];
{
struct interface *ifp;
struct mbuf *hbp;
int i;
char *cp;
if(argc < 2){
printf("Interface name missing\r\n");
return 1;
}
for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){
if(strcmp(argv[1],ifp->name) == 0)
break;
}
if(ifp == NULL){
printf("Interface \"%s\" unknown\r\n",argv[1]);
return 1;
}
if(ifp->output != kiss_output){
printf("Interface \"%s\" not kiss\r\n",argv[1]);
return 1;
}
if(argc < 3){
printf("Data field missing\r\n");
return 1;
}
/* Number of bytes in message == number of args - 2, since
* first two args are "kiss" and the interface name
*/
if((hbp = alloc_mbuf(argc - 2)) == NULLBUF){
free_p(hbp);
return;
}
hbp->cnt = argc - 2;
hbp->next = NULL;
for(i=2,cp = hbp->data;i < argc;i++,cp++){
*cp = htoi(argv[i]);
}
slipq(ifp->dev,hbp);
}
#ifdef TRACE
/* Dump an AX.25 packet header */
ax25_dump(abp)
struct mbuf *abp;
{
struct mbuf *bp;
struct ax25_addr src,dest,addr;
char tmp[20],*cp,control,cmdrsp,pid;
int16 len,type,ftype();
dup_p(&bp,abp,0,len_mbuf(abp));
/* Read and print the destination and source addresses */
if(pullup(&bp,(char *)&dest,AXALEN) != AXALEN)
goto quit;
if(pullup(&bp,(char *)&src,AXALEN) != AXALEN)
goto quit;
pax25(tmp,&src);
printf("AX25: %s",tmp);
pax25(tmp,&dest);
printf("->%s",tmp);
if((src.ssid & E) == 0){
/* Find the last address entry in the digi string */
printf(" v");
while(pullup(&bp,(char *)&addr,AXALEN) == AXALEN){
pax25(tmp,&addr);
#ifdef AMIGA
/* this is just personal preference; reminds me of
the WA8DED TNC-1 code */
printf(" %s%s",tmp,(addr.ssid & REPEATED) ? "*":"");
#else
printf(" %s%s",tmp,(addr.ssid & REPEATED) ? "H":"");
#endif
if(addr.ssid & E)
break; /* Found it */
}
}
if(pullup(&bp,&control,1) != 1)
goto quit;
putchar(' ');
type = ftype(control);
switch(type){
case I:
printf("I");
break;
case SABM:
printf("SABM");
break;
case DISC:
printf("DISC");
break;
case DM:
printf("DM");
break;
case UA:
printf("UA");
break;
case RR:
printf("RR");
break;
case RNR:
printf("RNR");
break;
case REJ:
printf("REJ");
break;
case FRMR:
printf("FRMR");
break;
case UI:
printf("UI");
break;
default:
printf("[invalid]");
}
if((dest.ssid & C) != (src.ssid & C)){
if(dest.ssid & C)
cmdrsp = COMMAND;
else
cmdrsp = RESPONSE;
} else
cmdrsp = UNKNOWN;
/* Dump poll/final bit */
if(control & PF){
switch(cmdrsp){
case COMMAND:
printf("(P)");
break;
case RESPONSE:
printf("(F)");
break;
default:
printf("(P/F)");
break;
}
}
/* Dump sequence numbers */
if((type & 0x3) != U) /* I or S frame? */
printf(" NR=%d",(control>>5)&7);
if((type & 0x1) == I) /* I frame? */
printf(" NS=%d",(control>>1)&7);
if(pullup(&bp,&pid,1) != 1)
goto quit;
printf(" pid 0x%x\r\n",pid & 0xff);
if((trace & TRACE_HDR) > 2){
switch(pid & 0xff){
case PID_ARP:
arp_dump(bp);
break;
case PID_IP:
ip_dump(bp);
break;
#ifdef NETROM
case PID_NETROM:
netrom_dump(bp);
break;
#endif
}
}
free_p(bp);
fflush(stdout);
return;
quit: /* I hate go-to's, but sometimes there's no real choice */
free_p(bp);
printf("\r\n");
fflush(stdout);
}
/* Figure out the frame type from the control field
* This is done by masking out any sequence numbers and the
* poll/final bit after determining the general class (I/S/U) of the frame
*/
static
int16
ftype(control)
register char control;
{
if((control & 1) == 0) /* An I-frame is an I-frame... */
return I;
if(control & 2) /* U-frames use all except P/F bit for type */
return(control & ~PF);
else /* S-frames use low order 4 bits for type */
return(control & 0xf);
}
#endif